﻿namespace EasyToolkit.BindingTemplates.Definitions.Binding
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Text.RegularExpressions;
    using EasyToolkit.BindingTemplates.Context;

    abstract public class BindingDefinition : IBindingDefinition
    {
        #region attributes and properties
        protected static Regex ValidCharExtract = new Regex("[a-zA-Z_]");

        protected BindingDefinitionDescription DefinitionDescription;

        public BindingPartType PartType
        { get { return BindingPartType.BindingDefinition; } }

        public string BindingExpression
        { get { return DefinitionDescription.BindingExpression; } }

        public bool IsACollection
        { get; protected set; }

        //public bool IsALinqCollection
        //{ get; protected set; }

        public Type BindingType
        { get; protected set; }

        public bool BindingTypeIsGeneric
        { get; protected set; }

        public Type BindingGenericType
        { get; protected set; }

        public Type BindingGenericTypeDefinition
        { get; protected set; }

        public Type CollectionType
        { get; protected set; }

        public bool IsReadOnly
        { get { return DefinitionDescription.IsReadOnly; } }

        public bool CanNotify
        { get; protected set; }

        public bool IsOptional
        { get; protected set; }

        public bool IsBoundWithData
        { get; protected set; }

        virtual public string Name
        { 
            get 
            {
                if (DefinitionDescription == null)
                    return string.Empty; 
                return string.IsNullOrEmpty(DefinitionDescription.Name) ? DefinitionDescription.BindingExpression : DefinitionDescription.Name; 
            } 
        }

        virtual public string Description
        { 
            get 
            {
                if (DefinitionDescription == null)
                    return string.Empty;
                return string.IsNullOrEmpty(DefinitionDescription.Description) ? Name : DefinitionDescription.Description; 
            } 
        }
        #endregion

        #region .ctors
        protected BindingDefinition()
        {
            CanNotify = false;
            IsBoundWithData = true;
        }
        #endregion

        public void ManageCollectionStatus()
        {
            IsACollection = BindingType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ICollection<>));
            if (IsACollection)
            {
                if (BindingType.IsArray)
                {
                    CollectionType = BindingType;
                    BindingType = BindingType.GetElementType();
                }
                else
                {
                    Type[] types = BindingType.GetGenericArguments();
                    if (types == null || types.Count() != 1)
                        throw new BindingTemplateException(string.Format("'{0}': Only collection with one generic argument are taken into account.", BindingType.FullName));
                    CollectionType = BindingType;
                    BindingType = types[0];
                }
            }
        }

        abstract public object UpdateDataSource(object dataSource, object data);

        abstract public object ResolveBinding(object dataSource);

        public virtual bool MustNotify(object dataSource, object source, PropertyChangedEventArgs args)
        {
            return false;
        }

        public virtual IEnumerable<INotifyPropertyChanged> GetObjectsToNotify(object dataSource)
        {
            return null;
        }

        public virtual IBindingContextItem ContextItemFactory(IBindingContextElement parent)
        {
            BindingContextItem ret;
            if (parent.DataSource == null)
                ret = new BindingContextItem(parent, this);
            else
            {
                if (CanNotify)
                {
                    BindingContextItemCanNotify contextItem = new BindingContextItemCanNotify(parent, this);
                    ret = contextItem;
                }
                else
                    ret = new BindingContextItem(parent, this);
            }

            ret.Init();
            return ret;
        }
    }
}